package org.dalmatian.common.rateLimiter.interceptor;

import org.dalmatian.common.core.exception.ServiceException;
import org.dalmatian.common.core.utils.MessageUtils;
import org.dalmatian.common.core.constants.GlobalConstants;
import org.dalmatian.common.core.utils.StringUtils;
import org.dalmatian.common.rateLimiter.annotation.RateLimiter;
import org.dalmatian.common.rateLimiter.enums.LimitType;
import org.dalmatian.common.redis.utils.RedisUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;

import org.noear.solon.core.aspect.Interceptor;
import org.noear.solon.core.aspect.Invocation;
import org.noear.solon.core.handle.Context;
import org.noear.solon.expression.snel.SnEL;
import org.redisson.api.RateType;

/**
 * 限流处理
 *
 * @author Lion Li
 */
@Slf4j
public class RateLimiterInterceptor implements Interceptor {


    @Override
    public Object doIntercept(Invocation inv) throws Throwable {
        RateLimiter anno = inv.getMethodAnnotation(RateLimiter.class);
        if (anno == null) {
            anno = inv.getTargetAnnotation(RateLimiter.class);
        }
        if (anno == null) {
            return inv.invoke();
        } else {
            int time = anno.time();
            int count = anno.count();
            int timeout = anno.timeout();
            String combineKey = getCombineKey(anno, inv);
            RateType rateType = RateType.OVERALL;
            if (anno.limitType() == LimitType.CLUSTER) {
                rateType = RateType.PER_CLIENT;
            }
            long number = RedisUtils.rateLimiter(combineKey, rateType, count, time, timeout);
            if (number == -1) {
                String message = anno.message();
                if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
                    message = MessageUtils.message(
                        StringUtils.substring(message, 1, message.length() - 1));
                }
                throw new ServiceException(message);
            }
            log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey);
            return inv.invoke();

        }

    }

    private String getCombineKey(RateLimiter rateLimiter, Invocation inv) {
        String key = rateLimiter.key();
        // 判断 key 不为空 和 不是表达式
        if (StringUtils.isNotBlank(key)) {
            Pattern pattern = Pattern.compile("^\\s*#");
            Matcher matcher = pattern.matcher(key);
            boolean isValid = matcher.find();
            if(!isValid){
                throw  new ServiceException("表达式# 前不得有参数");
            }
        }
        key = String.valueOf(SnEL.eval(key.replaceAll("#",""),inv.argsAsMap()));
        StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY);
        stringBuffer.append(Context.current().path()).append(":");
        if (rateLimiter.limitType() == LimitType.IP) {
            // 获取请求ip
            stringBuffer.append(Context.current().realIp()).append(":");
        } else if (rateLimiter.limitType() == LimitType.CLUSTER) {
            // 获取客户端实例id
            stringBuffer.append(RedisUtils.getClient().getId()).append(":");
        }
        return stringBuffer.append(key).toString();
    }
}
